package com.ccx.demo.business.code.controller;

import com.ccx.demo.business.code.dto.TabCodeExampleInsertDTO;
import com.ccx.demo.business.code.dto.TabCodeExampleUpdateDTO;
import com.ccx.demo.business.code.entity.TabCodeExample;
import com.ccx.demo.business.code.service.CodeExampleService;
import com.ccx.demo.business.code.vo.TabCodeExampleVO;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import com.github.xiaoymin.knife4j.annotations.DynamicParameter;
import com.github.xiaoymin.knife4j.annotations.DynamicParameters;
import com.support.mvc.encrypt.AesApiId;
import com.support.mvc.entity.IUser;
import com.support.mvc.entity.base.Page;
import com.support.mvc.entity.base.Result;
import com.utils.enums.Code;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.Cleanup;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import springfox.documentation.annotations.ApiIgnore;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static com.utils.util.Dates.Pattern.yyyyMMdd;

/**
 * 对外接口：测试案例表
 *
 * @author 谢长春 on 2022-02-16 V20220301
 */
@Api(tags = "测试案例表", hidden = true)
//@ApiSort() // 控制接口排序
@RequestMapping("/code-example")
@Controller
@Slf4j
@RequiredArgsConstructor
public class CodeExampleController {

    private final CodeExampleService service;

    @GetMapping("/page/{number}/{limit}")
    //@PreAuthorize("hasAnyAuthority('ROLE_ADMIN', 'ROLE_ADMIN_TEST', '{}_load')")
    @ApiOperation(value = "1.分页查询测试案例表", tags = {""})
    @ApiOperationSupport(
            order = 1,
            author = "谢长春",
            includeParameters = {"number", "limit", "orderBy", "name", "status", "createUserId", "updateUserId", "deleted"}
    )
    @ResponseBody
    public Result<TabCodeExample> page(
            @ApiIgnore @AuthenticationPrincipal final IUser user,
            @ApiParam(required = true, value = "页码", example = "1") @PathVariable final int number,
            @ApiParam(required = true, value = "每页条数，最大值1000", example = "10") @PathVariable final int limit,
            final TabCodeExample condition) {
        return new Result<TabCodeExample>().execute(result -> { // Controller 方法逻辑写在这里
            result.setSuccess(service.page(condition, Page.of(number, limit)));
        });
    }

//// 非必要情况下不要开放列表查询方法，因为没有分页控制，容易内存溢出。大批量查询数据应该使用分页查询
//    @GetMapping
//    // @PreAuthorize("hasAnyAuthority('ROLE_ADMIN', 'ROLE_ADMIN_TEST', '{}_load')")
//    @ApiOperation(value = "2.列表查询测试案例表", tags = {""})
//    @ApiOperationSupport(
//            order = 2,
//            author = "谢长春",
//            includeParameters = {"orderBy", "name", "status", "createUserId", "updateUserId", "deleted"}
//    )
//    @ResponseBody
//    public Result<TabCodeExample> list(@ApiIgnore @AuthenticationPrincipal final IUser user, final TabCodeExample condition) {
//        return new Result<TabCodeExample>().execute(result -> { // Controller 方法逻辑写在这里
//            result.setSuccess(service.list( condition ));
//        });
//    }
//

    @GetMapping("/{encryptId}")
    //@PreAuthorize("hasAnyAuthority('ROLE_ADMIN', 'ROLE_ADMIN_TEST', '{}_load')")
    @ApiOperation(value = "3.按 id 查询测试案例表", tags = {""})
    @ApiOperationSupport(order = 3, author = "谢长春")
    @ResponseBody
    public Result<TabCodeExample> findById(
            @ApiIgnore @AuthenticationPrincipal final IUser user,
            @ApiParam(required = true, value = "加密id", example = "encryptId") @PathVariable final String encryptId
    ) {
        return new Result<TabCodeExample>().execute(result -> { // Controller 方法逻辑写在这里
            final AesApiId.AesId decrypt = AesApiId.decrypt(encryptId);
            result.setSuccess(service.findById(decrypt.getLongId()).orElse(null));
        });
    }

    @PostMapping
    //@PreAuthorize("hasAnyAuthority('ROLE_ADMIN', 'ROLE_ADMIN_TEST', '{}_insert')")
    @ApiOperation(value = "4.新增测试案例表", tags = {""})
    @ApiOperationSupport(
            order = 4,
            author = "谢长春",
            includeParameters = {
                    "body.name", "body.content", "body.contentEncode", "body.amount", "body.status"
            })
    @ResponseBody
    public Result<TabCodeExample> insert(
            @ApiIgnore @AuthenticationPrincipal final IUser user,
            @RequestBody final TabCodeExampleInsertDTO body
    ) {
        return new Result<TabCodeExample>().execute(result -> { // Controller 方法逻辑写在这里
            result.setSuccess(service.insert(body, user.getId()));
        });
    }

    @PutMapping("/{encryptId}")
    //@PreAuthorize("hasAnyAuthority('ROLE_ADMIN', 'ROLE_ADMIN_TEST', '{}_update')")
    @ApiOperation(value = "5.修改测试案例表", tags = {""})
    @ApiOperationSupport(
            order = 5,
            author = "谢长春",
            ignoreParameters = {
                    "body.name", "body.content", "body.contentEncode", "body.amount", "body.status"
            })
    @ResponseBody
    public Result<Void> update(
            @ApiIgnore @AuthenticationPrincipal final IUser user,
            @ApiParam(required = true, value = "加密id", example = "encryptId") @PathVariable final String encryptId,
            @RequestBody final TabCodeExampleUpdateDTO body
    ) {
        return new Result<Void>().call(() -> { // Controller 方法逻辑写在这里
            final AesApiId.AesId decrypt = AesApiId.decrypt(encryptId);
            body.setUpdateTime(decrypt.getUpdateTime());
            service.update(decrypt.getLongId(), user.getId(), body);
        });
    }

    @PatchMapping("/{encryptId}")
    //@PreAuthorize("hasAnyAuthority('ROLE_ADMIN', 'ROLE_ADMIN_TEST', '{}_delete')")
    @ApiOperation(value = "6.逻辑删除测试案例表", tags = {""})
    @ApiOperationSupport(order = 6, author = "谢长春")
    @ResponseBody
    public Result<Void> markDeleteById(
            @ApiIgnore @AuthenticationPrincipal final IUser user,
            @ApiParam(required = true, value = "加密id", example = "encryptId") @PathVariable final String encryptId
    ) {
        return new Result<Void>().call(() -> {
            final AesApiId.AesId decrypt = AesApiId.decrypt(encryptId);
            service.markDeleteById(decrypt.getLongId(), user.getId());
        });
    }

    @PatchMapping
    //@PreAuthorize("hasAnyAuthority('ROLE_ADMIN', 'ROLE_ADMIN_TEST', '{}_delete')")
    @ApiOperation(value = "7.逻辑删除测试案例表", tags = {""})
    @ApiOperationSupport(
            order = 7,
            author = "谢长春",
            params = @DynamicParameters(name = "EncryptIdArray", properties = {
                    @DynamicParameter(name = "EncryptIdArray", value = "加密id", example = "[encryptId,encryptId]", required = true, dataTypeClass = String[].class)
            })
    )
    @ResponseBody
    public Result<Void> markDeleteByIds(
            @ApiIgnore @AuthenticationPrincipal final IUser user,
            @RequestBody final Set<String> body
    ) {
        return new Result<Void>().call(() -> {
            Code.A00002.assertNonEmpty(body, "请至少选择一条数据");
            final List<AesApiId.AesId> decryptList = body.stream().map(AesApiId::decrypt).collect(Collectors.toList());
            service.markDeleteByIds(
                    decryptList.stream().map(AesApiId.AesId::getLongId).collect(Collectors.toSet())
                    , decryptList.stream().map(AesApiId.AesId::getUpdateTime).collect(Collectors.toSet())
                    , user.getId()
            );
        });
    }

    @DeleteMapping("/{encryptId}")
    //@PreAuthorize("hasAnyAuthority('ROLE_ADMIN', 'ROLE_ADMIN_TEST', '{}_delete')")
    @ApiOperation(value = "8.物理删除测试案例表", tags = {""})
    @ApiOperationSupport(order = 8, author = "谢长春")
    @ResponseBody
    public Result<Void> deleteById(
            @ApiIgnore @AuthenticationPrincipal final IUser user,
            @ApiParam(required = true, value = "加密id", example = "encryptId") @PathVariable final String encryptId) {
        return new Result<Void>().call(() -> {
            final AesApiId.AesId decrypt = AesApiId.decrypt(encryptId);
            service.deleteById(decrypt.getLongId(), user.getId());
        });
    }

    @DeleteMapping
    //@PreAuthorize("hasAnyAuthority('ROLE_ADMIN', 'ROLE_ADMIN_TEST', '{}_delete')")
    @ApiOperation(value = "9.物理删除测试案例表", tags = {""})
    @ApiOperationSupport(
            order = 9,
            author = "谢长春",
            params = @DynamicParameters(name = "EncryptIdArray", properties = {
                    @DynamicParameter(name = "EncryptIdArray", value = "加密id", example = "[encryptId,encryptId]", required = true, dataTypeClass = String[].class)
            })
    )
    @ResponseBody
    public Result<Void> deleteByIds(
            @ApiIgnore @AuthenticationPrincipal final IUser user,
            @RequestBody final Set<String> body
    ) {
        return new Result<Void>().call(() -> {
            Code.A00002.assertNonEmpty(body, "请至少选择一条数据");
            final List<AesApiId.AesId> decryptList = body.stream().map(AesApiId::decrypt).collect(Collectors.toList());
            service.deleteByIds(
                    decryptList.stream().map(AesApiId.AesId::getLongId).collect(Collectors.toSet())
                    , decryptList.stream().map(AesApiId.AesId::getUpdateTime).collect(Collectors.toSet())
                    , user.getId()
            );
        });
    }

    @GetMapping("/page/vo/{number}/{limit}")
    @ApiOperation(value = "11.扩展查询条件和返回参数", tags = {""})
    @ApiOperationSupport(order = 11, author = "谢长春")
    @ResponseBody
    public Result<TabCodeExampleVO> pageVO(
            @ApiIgnore @AuthenticationPrincipal final IUser user,
            @ApiParam(required = true, value = "页码", example = "1") @PathVariable final int number,
            @ApiParam(required = true, value = "每页条数", example = "1") @PathVariable final int limit,
            final TabCodeExampleVO condition) {
        return new Result<TabCodeExampleVO>().execute(result ->
                result.setSuccess(service.pageVO(condition, Page.of(number, limit)))
        );
    }


    @GetMapping()
    @ApiOperation(value = "12.扩展查询条件和返回参数", tags = {""})
    @ApiOperationSupport(order = 12, author = "谢长春")
    @ResponseBody
    public Result<TabCodeExample> list(
            @ApiIgnore @AuthenticationPrincipal final IUser user,
            final TabCodeExample condition) {
        return new Result<TabCodeExample>().execute(result -> result.setSuccess(service.list(condition)));
    }

    @GetMapping("/vo")
    @ApiOperation(value = "12.扩展查询条件和返回参数", tags = {""})
    @ApiOperationSupport(order = 12, author = "谢长春")
    @ResponseBody
    public Result<TabCodeExampleVO> listVO(
            @ApiIgnore @AuthenticationPrincipal final IUser user,
            final TabCodeExampleVO condition) {
        return new Result<TabCodeExampleVO>().execute(result -> result.setSuccess(service.listVO(condition)));
    }

    @ApiIgnore
    @GetMapping("/test")
    @ResponseBody
    public Result<Void> test(@ApiIgnore @AuthenticationPrincipal final IUser user) {
        return new Result<Void>().call(service::listTest);
    }

    @GetMapping("/page/simple/{number}/{limit}")
    @ApiOperation(value = "13.连表查询指定字段", tags = {""})
    @ApiOperationSupport(order = 13, author = "谢长春")
    @ResponseBody
    public Result<TabCodeExampleVO> pageSimpleJoinTabUser(
            @ApiIgnore @AuthenticationPrincipal final IUser user,
            @ApiParam(required = true, value = "页码", example = "1") @PathVariable final int number,
            @ApiParam(required = true, value = "每页条数", example = "1") @PathVariable final int limit,
            final TabCodeExampleVO condition) {
        return new Result<TabCodeExampleVO>().execute(result -> result.setSuccess(service.pageSimpleJoinTabUser(
                condition,
                Page.builder().number(number).limit(limit).build())
        ));
    }

    @GetMapping("/page/full/{number}/{limit}")
    @ApiOperation(value = "14.连表查询主表全部字段", tags = {""})
    @ApiOperationSupport(order = 14, author = "谢长春")
    @ResponseBody
    public Result<TabCodeExampleVO> pageFullJoinTabUser(
            @ApiIgnore @AuthenticationPrincipal final IUser user,
            @ApiParam(required = true, value = "页码", example = "1") @PathVariable final int number,
            @ApiParam(required = true, value = "每页条数", example = "1") @PathVariable final int limit,
            final TabCodeExampleVO condition) {
        return new Result<TabCodeExampleVO>().execute(result -> result.setSuccess(service.pageFullJoinTabUser(
                condition,
                Page.builder().number(number).limit(limit).build())
        ));
    }

    @GetMapping("/csv")
    @ApiOperation(value = "15.导出csv格式", tags = {""})
    @ApiOperationSupport(order = 15, author = "谢长春")
    public void csv(
            @ApiIgnore @AuthenticationPrincipal final IUser user
            , final TabCodeExampleVO condition
            , final HttpServletResponse response) {
        try {
            final String fileName = URLEncoder.encode(String.format("文件导出测试_%s.csv", yyyyMMdd.now()), StandardCharsets.UTF_8.displayName());
            response.setHeader("Content-disposition", "attachment; filename=" + fileName);// 设定输出文件头
            response.setContentType("text/csv;charset=UTF-8");// 定义输出类型
            @Cleanup final ServletOutputStream outputStream = response.getOutputStream();
            outputStream.write(new byte[]{(byte) 0xEF, (byte) 0xBB, (byte) 0xBF}); // 加上UTF-8文件的标识字符， 解决文件内容乱码
            final byte[] newLine = "\r\n".getBytes();
            @Cleanup final BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(outputStream);
            bufferedOutputStream.write(String
                    .join(","
                            , "id"
                            , "name"
                            , "amount"
                            , "status"
                            , "createUserId"
                            , "createTime"
                            , "updateUserId"
                            , "updateTime"
                    )
                    .getBytes(StandardCharsets.UTF_8)
            );
            bufferedOutputStream.write(newLine);
            service.forEachCsv(condition, list -> list.forEach(row -> {
                try {
                    bufferedOutputStream.write(Stream
                            .of(
                                    row.getId()
                                    , row.getName()
                                    , row.getAmount()
                                    , row.getStatus()
                                    , row.getCreateUserId()
                                    , row.getCreateTime()
                                    , row.getUpdateUserId()
                                    , row.getUpdateTime()
                            )
                            .map(val -> Objects.isNull(val) ? "" : val)
                            .map(Object::toString)
                            .map(val -> val.replaceAll(",", "，"))
                            .collect(Collectors.joining(","))
                            .getBytes(StandardCharsets.UTF_8)
                    );
                    bufferedOutputStream.write(newLine);
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }));
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }

    }

}
